﻿
using System.Diagnostics;
using System.Reflection;

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;


namespace zijian666.DI;
public static class ConfigurationExtensions
{


    /// <summary>
    /// 获取配置方法（无参或者只有一个<seealso cref="IConfigurationSection"/>参数的实例或静态方法）
    /// </summary>
    private static IEnumerable<MethodInfo> GetConfigurationMethods(this object obj)
    {
        // 获取无参的或者只有一个IConfigurationSection参数的方法, 如果有重载, 则保留有参数的那个
        return obj.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
            .Where(x => !x.IsGenericMethod && !x.IsSpecialName && x.IsDefined(typeof(BindConfigurationAttribute)))
            .Where(x => x.GetParameters() is { Length: 0 or 1 })
            .GroupBy(x => x.Name)
            .Select(x => x.OrderByDescending(y => y.GetParameters().Length).First());
    }

    /// <summary>
    /// 判断配置节点是否被禁用
    /// </summary>
    /// <param name="section">需判断的配置节点</param>
    /// <param name="defaultSection">默认配置节点</param>
    /// <returns>
    /// 如果配置节点存在, 则配置节点的值应等于 false; 如果配置节点不存在, 则默认配置节点的值应等于 false
    /// </returns>
    private static bool? IsDisabled(this IConfigurationSection section)
    {
        if (section.Exists())
        {
            return section.Value?.ToLower() == "false";
        }
        return null;
    }

    /// <summary>
    /// 根据配置文件调用 <paramref name="instance"/> 中的对应方法, 必要时传入配置节点
    /// </summary>
    /// <param name="configuration">配置</param>
    /// <param name="instance">对象实例</param>
    /// <param name="defaultSectionKey">默认配置节前缀</param>
    [Obsolete("方法改名为 Configure ")]
    public static void BindTo(this IConfiguration configuration, object instance, string? defaultSectionKey = null)
    {
        ArgumentNullException.ThrowIfNull(configuration);
        ArgumentNullException.ThrowIfNull(instance);
        if (string.IsNullOrWhiteSpace(defaultSectionKey))
        {
            ConfigureCore(configuration, configuration, instance);
        }
        else
        {
            ConfigureCore(configuration, configuration.GetSection(defaultSectionKey), instance);
        }
    }

    public static void Configure(this IConfiguration configuration, string sectionKey, object instance, Action<ConfigurationOptions>? action = null)
    {
        ArgumentNullException.ThrowIfNull(configuration);
        ArgumentNullException.ThrowIfNull(instance);
        if (string.IsNullOrWhiteSpace(sectionKey))
        {
            throw new ArgumentException($"“{nameof(sectionKey)}”不能为 null 或空白。", nameof(sectionKey));
        }

        ConfigureCore(configuration, configuration.GetSection(sectionKey), instance, action);
    }

    public static void Configure(this IConfiguration configuration, object instance, Action<ConfigurationOptions>? action = null)
    {
        ArgumentNullException.ThrowIfNull(configuration);
        ArgumentNullException.ThrowIfNull(instance);
        ConfigureCore(configuration, configuration, instance, action);
    }

    private static void ConfigureCore(IConfiguration root, IConfiguration configuration, object instance, Action<ConfigurationOptions>? action = null)
    {
        var options = new ConfigurationOptions();
        action?.Invoke(options);
        var defaultSection = configuration.GetSection("*");

        var logger = options.Logger ?? options.LoggerFactory?.CreateLogger(nameof(ConfigurationExtensions));
        var className = instance.GetType().Name;
        logger?.LogInformation("Configure: {className}", instance.GetType().FullName ?? className);
        foreach (var method in instance.GetConfigurationMethods())
        {
            var attr = method.GetCustomAttribute<BindConfigurationAttribute>();
            // 获取指定配置
            var section = string.IsNullOrWhiteSpace(attr?.ConfigurationKeyName)
                            ? configuration.GetSection(method.Name)
                            : root.GetSection(attr.ConfigurationKeyName);
            if (!section.Exists() && defaultSection.Exists())
            {
                section = defaultSection;
            }

            // 如果配置节点被禁用, 则直接返回
            if (section.IsDisabled() ?? attr?.Disabled ?? true)
            {
                var reason = section.IsDisabled() == true ? "Disabled" : "Default";

                logger?.LogInformation("Skip: {className}.{method}(\"{section}\") {reason}", className, method.Name, section.Path, reason);
                continue;
            }
            var timer = Stopwatch.StartNew();
            if (instance.Invoke(method, root, section, attr?.DefaultEnable ?? false))
            {
                logger?.LogInformation("Call: {className}.{method}(\"{section}\") {timer}ms", className, method.Name, section.Path, timer.ElapsedMilliseconds);
            }
            else
            {
                logger?.LogInformation("Skip: {className}.{method}(\"{section}\")", className, method.Name, section.Path);
            }
        }
    }

    /// <summary>
    /// 根据配置动态调用方法
    /// </summary>
    /// <param name="instance">启动类实例</param>
    /// <param name="method">应调用的配置方法</param>
    /// <param name="section">配置节点</param>
    /// <param name="required">必须执行</param>
    /// <exception cref="InvalidOperationException">
    /// 当配置节点不存在, 且方法参数没有默认值时抛出
    /// </exception>
    private static bool Invoke(this object instance, MethodInfo method, IConfiguration root, IConfigurationSection section, bool required)
    {
        var parameters = method.GetParameters();
        // 如果参数是0个,则直接调用方法
        if (parameters.Length == 0)
        {
            method.Invoke(instance, []);
            return true;
        }
        // 如果参数不存在默认值, 且配置节点不存在
        if (!parameters[0].HasDefaultValue && !section.Exists())
        {
            if (required)
            {
                throw new InvalidOperationException($"Missing ConfigurationSection Method: {method.Name}, Path:{section.Path}");
            }
            return false;
        }
        try
        {
            if (!section.Exists())
            {
                method.Invoke(instance, [parameters[0].DefaultValue]);
                return true;
            }

            if (parameters[0].ParameterType == typeof(IConfiguration))
            {
                method.Invoke(instance, [root]);
                return true;
            }
            if (parameters[0].ParameterType.IsInstanceOfType(section))
            {
                method.Invoke(instance, [section]);
                return true;
            }
            var arg = section.Get(parameters[0].ParameterType, x =>
            {
                x.BindNonPublicProperties = true;
                x.ErrorOnUnknownConfiguration = true;
            });
            if (arg is null && !parameters[0].HasDefaultValue)
            {
                // 节点值无法转换为指定类型，2种情况下抛出异常
                // 1. 节点必须执行
                // 2. 节点不是默认节点, 说明在用户确认节点名称的情况下被设置了错误的值
                if (required || section.Key != "*")
                {
                    throw new InvalidCastException($"ConfigurationSection Type Error, Method: {method.Name}, Path: {section.Path}, Type: {parameters[0].ParameterType}");
                }
                return false;
            }
            arg ??= parameters[0].DefaultValue;
            method.Invoke(instance, [arg]);
            return true;
        }
        catch (Exception e) when (e is not InvalidCastException)
        {
            throw new InvalidOperationException($"ConfigurationSection Error, Method: {method.Name}, Path: {section.Path}, {e.Message}", e);
        }
    }

}
